﻿using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Beetle.Sl.EditorControl.Core;
using System.Windows.Interactivity;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using SLaB.Utilities.Xaml.Collections;
using System.Windows.Markup;
using SLaB.Utilities.Xaml.Serializer.UI;
using System.Collections;
using Beetle.Sl.EditorControl.Controls;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Data;

namespace Beetle.Sl.EditorControl
{
    [TemplateVisualStateAttribute(Name = "InvalidFocused", GroupName = "ValidationStates")]
    [TemplateVisualStateAttribute(Name = "InvalidUnfocused", GroupName = "ValidationStates")]
    [TemplateVisualStateAttribute(Name = "Valid", GroupName = "ValidationStates")]
    [TemplatePart(Name = "RichTextBoxInst", Type = typeof(RichTextBox))]
    public class Editor : Control
    {
        private Dictionary<string, IDocCommand> _docCommands;
        private System.Collections.ObjectModel.ObservableCollection<ToolBarGroup> _toolBarGroups;
        private List<IToolBarItem> _srcToolBarItems;
        private readonly object docResourcesLock = new object();
        private Dictionary<string, byte[]> _docResources;
        private byte[] internalSource = null;
        private string internalXaml = null;
        public event EventHandler EditorStatusChanged;

        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source",
           typeof(byte[]),
           typeof(Editor),
           new PropertyMetadata(null, new PropertyChangedCallback(Editor.OnSourcePropertyChanged)));

        public byte[] Source
        {
            get
            {
                return (byte[])this.GetValue(Editor.SourceProperty);
            }

            set
            {
                this.SetValue(Editor.SourceProperty, value);
            }
        }

        public static readonly DependencyProperty RichTextBoxBackgroundProperty = DependencyProperty.Register("RichTextBoxBackground",
           typeof(Brush),
           typeof(Editor),
           new PropertyMetadata(null, null));

        public Brush RichTextBoxBackground
        {
            get
            {
                return (Brush)this.GetValue(Editor.RichTextBoxBackgroundProperty);
            }

            set
            {
                this.SetValue(Editor.RichTextBoxBackgroundProperty, value);
            }
        }

        public static readonly DependencyProperty XamlProperty = DependencyProperty.Register("Xaml",
           typeof(string),
           typeof(Editor),
           new PropertyMetadata(null, new PropertyChangedCallback(Editor.OnXamlPropertyChanged)));

        public string Xaml
        {
            get
            {
                return (string)this.GetValue(Editor.XamlProperty);
            }

            set
            {
                this.SetValue(Editor.XamlProperty, value);
            }
        }

        public static readonly DependencyProperty ToolBarVisibilityProperty = DependencyProperty.Register("ToolBarVisibility",
           typeof(Visibility),
           typeof(Editor),
           new PropertyMetadata(Visibility.Visible, new PropertyChangedCallback(Editor.OnToolBarVisibilityPropertyChanged)));

        public Visibility ToolBarVisibility
        {
            get
            {
                return (Visibility)this.GetValue(Editor.ToolBarVisibilityProperty);
            }

            set
            {
                this.SetValue(Editor.ToolBarVisibilityProperty, value);
            }
        }

        public static readonly DependencyProperty FontFamilyNamesProperty = DependencyProperty.Register("FontFamilyNames",
           typeof(FontFamilyInfo[]),
           typeof(Editor),
           new PropertyMetadata(null, new PropertyChangedCallback(Editor.OnFontFamilyNamesPropertyChanged)));

        [TypeConverter(typeof(FontFamilyInfosTypeConverter))]
        public FontFamilyInfo[] FontFamilyNames
        {
            get
            {
                return (FontFamilyInfo[])this.GetValue(Editor.FontFamilyNamesProperty);
            }

            set
            {
                this.SetValue(Editor.FontFamilyNamesProperty, value);
            }
        }

        public static readonly DependencyProperty ToolBarGroupsConfigProperty = DependencyProperty.Register("ToolBarGroupsConfig",
           typeof(string),
           typeof(Editor),
           new PropertyMetadata(null, null));

        public string ToolBarGroupsConfig
        {
            get
            {
                return (string)this.GetValue(Editor.ToolBarGroupsConfigProperty);
            }

            set
            {
                this.SetValue(Editor.ToolBarGroupsConfigProperty, value);
            }
        }

        public static readonly DependencyProperty EditorStatusProperty = DependencyProperty.Register("EditorStatus",
          typeof(EditorStatusEnum),
          typeof(Editor),
          new PropertyMetadata(EditorStatusEnum.Edit, new PropertyChangedCallback(Editor.OnEditorStatusPropertyChanged)));

        public EditorStatusEnum EditorStatus
        {
            get
            {
                return (EditorStatusEnum)this.GetValue(Editor.EditorStatusProperty);
            }

            set
            {
                this.SetValue(Editor.EditorStatusProperty, value);
            }
        }

        public ObservableCollection<ToolBarGroup> ToolBarGroups
        {
            get
            {
                if (this._toolBarGroups == null)
                {
                    this._toolBarGroups = new ObservableCollection<ToolBarGroup>();
                }

                return this._toolBarGroups;
            }
        }

        internal Dictionary<string, byte[]> DocResources
        {
            get
            {
                if (this._docResources == null)
                {
                    lock (this.docResourcesLock)
                    {
                        if (this._docResources == null)
                        {
                            this._docResources = new Dictionary<string, byte[]>();
                        }
                    }
                }

                return this._docResources;
            }
        }

        internal RichTextBox RichTextBoxInst
        {
            get;
            private set;
        }

        Dictionary<string, IDocCommand> DocCommands
        {
            get
            {
                if (this._docCommands == null)
                {
                    this._docCommands = new Dictionary<string, IDocCommand>();
                }

                return this._docCommands;
            }
        }

        public List<IToolBarItem> srcToolBarItems
        {
            get
            {
                if (this._srcToolBarItems == null)
                {
                    this._srcToolBarItems = new List<IToolBarItem>();
                }

                return this._srcToolBarItems;
            }
        }

        public Editor()
        {
            this.DefaultStyleKey = typeof(Beetle.Sl.EditorControl.Editor);

            if (!DesignerProperties.IsInDesignTool)
            {
                this.Loaded += new RoutedEventHandler(this.InitEditor);
            }
        }

        private void InitEditor(object sender, RoutedEventArgs e)
        {
            this.Loaded -= new RoutedEventHandler(this.InitEditor);
            this.LoadCommandLib();
            this.LoadToolBarLib();
            this.LoadToolBarGroups();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (!DesignerProperties.IsInDesignTool)
            {
                this.RichTextBoxInst = base.GetTemplateChild("RichTextBoxInst") as RichTextBox;
                this.UpdateVisual();
                this.RichTextBoxInst.SelectionChanged += new RoutedEventHandler(RichTextBoxInst_SelectionChanged);
                // this.BindingValidationError += new EventHandler<ValidationErrorEventArgs>(Editor_BindingValidationError);
                this.RaiseEditorStatusChanged();
            }

            this.SetXaml(this.Xaml);
        }

        //void Editor_BindingValidationError(object sender, ValidationErrorEventArgs e)
        //{
        //    //if (e.Error == null)
        //    //{
        //    //    MessageBox.Show("Null");
        //    //}
        //    //else
        //    //{
        //    //    MessageBox.Show(e.Error.Exception.Message);
        //    //}

        //    ////  this.RichTextBoxInst.SetValue(Validation.ErrorsProperty, e.Error);
        //    //VisualStateManager.GoToState(this.RichTextBoxInst, "InvalidFocused", false);

        //    VisualStateManager.GoToState(this, "InvalidFocused", false);
        //}

        void RichTextBoxInst_SelectionChanged(object sender, RoutedEventArgs e)
        {
            this.UpdateVisual();
        }

        private static void OnSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Editor editor = d as Editor;

            byte[] newValue = (byte[])e.NewValue;
            byte[] oldValue = (byte[])e.OldValue;

            if (newValue != oldValue && newValue != editor.internalSource)
            {
                editor.RaiseSourceChanged(newValue);
            }
        }

        private static void OnXamlPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Editor editor = d as Editor;

            string newValue = (string)e.NewValue;
            string oldValue = (string)e.OldValue;

            if (newValue != oldValue && newValue != editor.internalXaml)
            {
                editor.RaiseXamlChanged(newValue);
            }
        }

        private static void OnToolBarVisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Editor editor = d as Editor;

            Visibility newValue = (Visibility)e.NewValue;
            Visibility oldValue = (Visibility)e.OldValue;

            if (newValue != oldValue)
            {
                editor.RaiseToolBarVisibilityChanged(newValue);
            }
        }

        private static void OnFontFamilyNamesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { }

        private static void OnEditorStatusPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Editor editor = d as Editor;

            EditorStatusEnum newValue = (EditorStatusEnum)e.NewValue;
            EditorStatusEnum oldValue = (EditorStatusEnum)e.OldValue;
            if (newValue != oldValue)
            {
                editor.RaiseEditorStatusChanged();
            }
        }

        private void RaiseSourceChanged(byte[] newValue)
        {
            this.internalSource = newValue;
            this.Source = newValue;

            //BindingExpression bindingExpression;// element.GetBindingExpression(dependencyProperty);
            //if (bindingExpression != null)
            //{
            //    bindingExpression.UpdateSource();
            //}

            this.SetZipBytes(newValue);
        }

        private void RaiseXamlChanged(string newValue)
        {
            this.internalXaml = newValue;
            this.Xaml = newValue;
            // this.SetValue(Editor.XamlProperty, newValue);
            this.SetXaml(newValue);
        }

        private void RaiseToolBarVisibilityChanged(Visibility newValue)
        { }

        private void RaiseEditorStatusChanged()
        {
            if (this.RichTextBoxInst != null)
            {
                switch (this.EditorStatus)
                {
                    case EditorStatusEnum.Edit:
                        this.RichTextBoxInst.IsReadOnly = false;
                        break;
                    case EditorStatusEnum.Designer:
                        this.RichTextBoxInst.IsReadOnly = true;
                        break;
                    case EditorStatusEnum.View:
                        this.RichTextBoxInst.IsReadOnly = true;
                        break;
                    default:
                        break;
                }

                this.UpdateVisual();

                if (this.EditorStatusChanged != null)
                {
                    this.EditorStatusChanged(this, null);
                }
            }
        }

        public void ExcuteCommand(string commandText, object parameter)
        {
            IDocCommand cmd = null;
            this.DocCommands.TryGetValue(commandText, out cmd);

            if (cmd != null)
            {
                cmd.Excute(parameter);
            }

            this.UpdateVisual();
        }

        private void LoadToolBarLib()
        {
            Type[] allTypes = this.GetType().Assembly.GetTypes();
            Assembly assembly = this.GetType().Assembly;
            Type itemType = typeof(IToolBarItem);
            Type attType = typeof(ToolBarItemAttribute);

            foreach (var item in allTypes)
            {
                object[] atts = item.GetCustomAttributes(attType, false);

                if (atts.Length == 1)
                {
                    IToolBarItem toolBarItem = assembly.CreateInstance(item.FullName) as IToolBarItem;
                    toolBarItem.Load(this);
                    this.srcToolBarItems.Add(toolBarItem);
                }
            }
        }

        private void LoadCommandLib()
        {
            Type[] allTypes = this.GetType().Assembly.GetTypes();
            Assembly assembly = this.GetType().Assembly;
            Type cmdType = typeof(IDocCommand);
            Type attType = typeof(CommandAttribute);

            foreach (var item in allTypes)
            {
                object[] atts = item.GetCustomAttributes(attType, false);

                if (atts.Length == 1)
                {
                    IDocCommand cmd = assembly.CreateInstance(item.FullName) as IDocCommand;
                    cmd.Load(this);
                    this.DocCommands.Add(cmd.CommandText, cmd);
                }
            }
        }

        private void LoadToolBarGroups()
        {
            string config = this.ToolBarGroupsConfig;
            if (string.IsNullOrWhiteSpace(config))
            {
                config = "OpenLocalFile,SaveAsLocalFile;SetFontFamily,SetFontSize,SetFontColor;TextAlignmentLeft,TextAlignmentCenter,TextAlignmentRight;SetFontBold,SetFontItalic,SetFontUnderline;InsertImage;EditorStatus";
            }

            string[] groups = config.Split(';');

            foreach (var group in groups)
            {
                ToolBarGroup toolBarGroup = new ToolBarGroup();
                if (!string.IsNullOrWhiteSpace(group))
                {
                    string[] items = group.Split(',');

                    foreach (var item in items)
                    {
                        IToolBarItem toolBarItem = this.srcToolBarItems.FirstOrDefault(o => o.Name == item);

                        if (toolBarItem != null)
                        {
                            toolBarGroup.Items.Add(toolBarItem);
                        }
                    }
                }

                this.ToolBarGroups.Add(toolBarGroup);
            }
        }

        private void UpdateVisual()
        {
            foreach (var item in this.ToolBarGroups)
            {
                foreach (var toolBar in item.Items)
                {
                    toolBar.UpdateVisual();
                }
            }
        }

        public byte[] GetZipBytes()
        {
            if (this.RichTextBoxInst.Blocks.Count == 0 || string.IsNullOrWhiteSpace(this.RichTextBoxInst.Xaml))
            {
                byte[] bytes = new byte[] { };
                this.internalSource = bytes;
                this.Source = bytes;
                return bytes;
            }

            UiXamlSerializer uxs = new UiXamlSerializer();
            ObservableObjectCollection ooc = new ObservableObjectCollection();
            foreach (Block b in this.RichTextBoxInst.Blocks)
            {
                ooc.Add(b);
            }

            string xaml = uxs.Serialize(ooc);

            MemoryStream memoryStream = new MemoryStream();
            ZipOutputStream zipOutputStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(memoryStream);
            zipOutputStream.SetLevel(6);
            zipOutputStream.IsStreamOwner = true;

            // xaml 
            ZipEntry zipEntry = new ZipEntry("main.xaml");
            zipEntry.DateTime = DateTime.Now;
            zipOutputStream.PutNextEntry(zipEntry);

            byte[] xamlBytes = System.Text.Encoding.Unicode.GetBytes(xaml);
            zipOutputStream.Write(xamlBytes, 0, xamlBytes.Length);

            // 资源
            foreach (Block b in this.RichTextBoxInst.Blocks)
            {
                Paragraph paragraph = b as Paragraph;

                if (paragraph != null)
                {
                    foreach (var item in paragraph.Inlines)
                    {
                        InlineUIContainer inlineUIContainer = item as InlineUIContainer;

                        if (inlineUIContainer != null && inlineUIContainer.Child != null)
                        {
                            IEditorControl editorControl = inlineUIContainer.Child as IEditorControl;

                            if (editorControl != null)
                            {
                                if (editorControl.IsHasResours())
                                {
                                    byte[] resBytes = editorControl.GetBytes();

                                    if (resBytes != null && !string.IsNullOrWhiteSpace(editorControl.ResId))
                                    {
                                        ZipEntry resEntry = new ZipEntry(string.Format("Res/{0}.dat", editorControl.ResId));
                                        resEntry.DateTime = DateTime.Now;
                                        zipOutputStream.PutNextEntry(resEntry);

                                        zipOutputStream.Write(resBytes, 0, resBytes.Length);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // 结束
            zipOutputStream.Finish();

            // 生成 bytes
            memoryStream.Seek(0, SeekOrigin.Begin);
            byte[] returnBytes = new byte[memoryStream.Length];
            memoryStream.Read(returnBytes, 0, returnBytes.Length);

            // 关闭
            zipOutputStream.Close();
            this.internalSource = returnBytes;
            this.Source = returnBytes;
            return returnBytes;
        }

        public void SetZipBytes(byte[] bytes)
        {
            this.UpdateLayout();

            this.DocResources.Clear();
            MemoryStream memoryStream = new MemoryStream();
            memoryStream.Write(bytes, 0, bytes.Length);
            memoryStream.Seek(0, SeekOrigin.Begin);

            ZipInputStream zipInputStream = new ZipInputStream(memoryStream);

            ZipEntry zipEntry = zipInputStream.GetNextEntry();
            while (zipEntry != null)
            {
                byte[] bytesTemp = new byte[zipEntry.Size];
                zipInputStream.Read(bytesTemp, 0, (int)zipEntry.Size);
                this.DocResources.Add(zipEntry.Name, bytesTemp);
                zipEntry = zipInputStream.GetNextEntry();
            }

            this.ShowZipDoc();
        }

        private void ShowZipDoc()
        {
            // 显示所有文本和框架
            byte[] xamlBytes = null;
            if (this.DocResources.TryGetValue("main.xaml", out xamlBytes))
            {
                string xaml = System.Text.Encoding.Unicode.GetString(xamlBytes, 0, xamlBytes.Length);
                ObservableObjectCollection bc = (ObservableObjectCollection)XamlReader.Load(xaml);

                // this.RichTextBoxInst.Xaml = string.Empty;
                this.RichTextBoxInst.Blocks.Clear();

                foreach (Block b in bc)
                {
                    Paragraph paragraph = b as Paragraph;

                    if (paragraph != null)
                    {
                        foreach (var item in paragraph.Inlines)
                        {
                            InlineUIContainer inlineUIContainer = item as InlineUIContainer;

                            if (inlineUIContainer != null && inlineUIContainer.Child != null)
                            {
                                IEditorControl editorControl = inlineUIContainer.Child as IEditorControl;

                                if (editorControl != null)
                                {
                                    if (editorControl.IsHasResours())
                                    {
                                        if (!string.IsNullOrWhiteSpace(editorControl.ResId))
                                        {
                                            byte[] resBytes = null;

                                            if (this.DocResources.TryGetValue(string.Format("Res/{0}.dat", editorControl.ResId), out resBytes))
                                            {
                                                editorControl.SetBytes(editorControl.ResId, resBytes);
                                            }
                                        }
                                    }

                                    editorControl.ResourceLoaded(this);
                                }
                            }
                        }
                    }

                    this.RichTextBoxInst.Blocks.Add(b);
                }
            }
        }

        public string GetXaml()
        {
            if (this.RichTextBoxInst.Blocks.Count == 0 || string.IsNullOrWhiteSpace(this.RichTextBoxInst.Xaml))
            {
                this.internalXaml = string.Empty;
                this.Xaml = string.Empty;
                return internalXaml;
            }

            // 资源
            foreach (Block b in this.RichTextBoxInst.Blocks)
            {
                Paragraph paragraph = b as Paragraph;

                if (paragraph != null)
                {
                    foreach (var item in paragraph.Inlines)
                    {
                        InlineUIContainer inlineUIContainer = item as InlineUIContainer;

                        if (inlineUIContainer != null && inlineUIContainer.Child != null)
                        {
                            IEditorControl editorControl = inlineUIContainer.Child as IEditorControl;

                            if (editorControl != null)
                            {
                                if (editorControl.IsHasResours())
                                {
                                    editorControl.Serialize();
                                }
                            }
                        }
                    }
                }
            }

            UiXamlSerializer uxs = new UiXamlSerializer();
            ObservableObjectCollection ooc = new ObservableObjectCollection();
            foreach (Block b in this.RichTextBoxInst.Blocks)
            {
                ooc.Add(b);
            }

            string xaml = uxs.Serialize(ooc);
            this.internalXaml = xaml;
            this.Xaml = xaml;
            return xaml;
        }

        public void SetXaml(string xaml)
        {
            if (this.RichTextBoxInst == null)
            {
                return;
            }

            this.RichTextBoxInst.Blocks.Clear();
            if (string.IsNullOrWhiteSpace(xaml))
            {
                return;
            }

            ObservableObjectCollection bc = (ObservableObjectCollection)XamlReader.Load(xaml);

            foreach (Block b in bc)
            {
                Paragraph paragraph = b as Paragraph;

                if (paragraph != null)
                {
                    foreach (var item in paragraph.Inlines)
                    {
                        InlineUIContainer inlineUIContainer = item as InlineUIContainer;

                        if (inlineUIContainer != null && inlineUIContainer.Child != null)
                        {
                            IEditorControl editorControl = inlineUIContainer.Child as IEditorControl;

                            if (editorControl != null)
                            {
                                if (editorControl.IsHasResours())
                                {
                                    if (!string.IsNullOrWhiteSpace(editorControl.ResString))
                                    {
                                        editorControl.SetXaml(editorControl.ResString);
                                    }
                                }

                                editorControl.ResourceLoaded(this);
                            }
                        }
                    }
                }

                this.RichTextBoxInst.Blocks.Add(b);
            }
        }
    }
}
